package com.kdgc.energy.dmm.entity;

import cn.hutool.core.util.StrUtil;
import com.kdgc.energy.dmm.util.NameUtil;

import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.Id;
import javax.persistence.Table;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.util.ArrayList;
import java.util.List;

/**
 * 实体类阅读器
 *
 * @author xu.wenchang
 * @version 1.0 2022/04/14
 */
public final class EntityClassReader {
    private final Class<?> clazz;
    
    public EntityClassReader(Class<?> entityClass) {
        this.clazz = entityClass;
    }
    
    /**
     * 读取实体类信息
     */
    public EntityInfo read() {
        String tableName = readTableName();
        
        if (tableName == null) {
            throw new IllegalArgumentException(this.clazz.getCanonicalName() + "缺失表名信息注解.");
        }
        
        EntityInfo entityInfo = new EntityInfo();
        entityInfo.setEntityClass(this.clazz);
        entityInfo.setTableName(tableName);
        
        List<FieldInfo> fieldList = readFields();
        List<MethodInfo> methodList = readMethods();
        
        if (fieldList.size() == 0 && methodList.size() == 0) {
            throw new IllegalArgumentException(this.clazz.getCanonicalName() + "缺失字段信息注解.");
        }
        
        entityInfo.addFieldInfo(fieldList);
        entityInfo.addMethodInfo(methodList);
        return entityInfo;
    }
    
    /**
     * 读取方法信息
     */
    private List<MethodInfo> readMethods() {
        Method[] methods = this.clazz.getMethods();
        List<MethodInfo> list = new ArrayList<>();
        
        for (Method m : methods) {
            if (!Modifier.isStatic(m.getModifiers())) {
                String fieldName = readFieldName(m);
                
                if (null == fieldName) {
                    continue;
                }
                
                MethodInfo methodInfo = new MethodInfo();
                methodInfo.setMethod(m);
                methodInfo.setColumnName(fieldName);
                methodInfo.setPk(m.isAnnotationPresent(Id.class));
                list.add(methodInfo);
            }
        }
        
        return list;
    }
    
    /**
     * 读取字段名
     */
    private String readFieldName(Method m) {
        if (m.isAnnotationPresent(Column.class)) {
            Column column = m.getDeclaredAnnotation(Column.class);
            
            if (StrUtil.isNotBlank(column.name())) {
                return column.name();
            }
            
            String name = m.getName()
                           .replaceAll("(get)|(set)", "");
            return NameUtil.camel2underline(name);
        }
        
        return null;
    }
    
    /**
     * 读取字段信息
     */
    private List<FieldInfo> readFields() {
        Field[] fields = this.clazz.getDeclaredFields();
        List<FieldInfo> list = new ArrayList<>();
        
        for (Field f : fields) {
            if (Modifier.isStatic(f.getModifiers())) {
                continue;
            }
            
            String fieldName = readFieldName(f);
            
            if (null == fieldName) {
                continue;
            }
            
            FieldInfo fieldInfo = new FieldInfo();
            fieldInfo.setField(f);
            fieldInfo.setColumnName(fieldName);
            fieldInfo.setPk(f.isAnnotationPresent(Id.class));
            list.add(fieldInfo);
        }
        
        return list;
    }
    
    /**
     * 读取字段名
     */
    private String readFieldName(Field f) {
        if (f.isAnnotationPresent(Column.class)) {
            Column column = f.getDeclaredAnnotation(Column.class);
            
            if (StrUtil.isNotBlank(column.name())) {
                return column.name();
            }
            
            return NameUtil.camel2underline(f.getName());
        }
        
        return null;
    }
    
    /**
     * 读取表名
     */
    private String readTableName() {
        if (this.clazz.isAnnotationPresent(Entity.class)) {
            Entity entity = this.clazz.getDeclaredAnnotation(Entity.class);
            
            if (StrUtil.isNotBlank(entity.name())) {
                return entity.name();
            }
        }
        
        if (this.clazz.isAnnotationPresent(Table.class)) {
            Table table = this.clazz.getDeclaredAnnotation(Table.class);
            
            if (StrUtil.isNotBlank(table.name())) {
                return table.name();
            }
        }
        
        return null;
    }
}
